package column

import (
	"fmt"
	"game/game-db/orm/metag"
	"reflect"
	"strings"
	"unsafe"
)

type PrimaryKeyColumnMeta struct {
	ColumnMeta
	strategy metag.KeyStrategy
	length   int32
	autoBase int64
}

func (t *PrimaryKeyColumnMeta) KeyStrategy() metag.KeyStrategy {
	return t.strategy
}

func (t *PrimaryKeyColumnMeta) TakeValue(objField reflect.Value) interface{} {
	if objField.CanInterface() {
		return objField.Interface()
	} else {
		objField = reflect.NewAt(objField.Type(), unsafe.Pointer(objField.UnsafeAddr())).Elem()
		return objField.Interface()
	}
}

func (t *PrimaryKeyColumnMeta) FillValue(objField reflect.Value, fieldValue reflect.Value) {
	if t.fieldKind == reflect.Int32 {
		if objField.CanSet() {
			objField.SetInt(int64(fieldValue.Interface().(int32)))
		} else {
			objField = reflect.NewAt(objField.Type(), unsafe.Pointer(objField.UnsafeAddr())).Elem()
			objField.SetInt(int64(fieldValue.Interface().(int32)))
		}
	} else if t.fieldKind == reflect.Int64 {
		if objField.CanSet() {
			objField.SetInt(fieldValue.Interface().(int64))
		} else {
			objField = reflect.NewAt(objField.Type(), unsafe.Pointer(objField.UnsafeAddr())).Elem()
			objField.SetInt(fieldValue.Interface().(int64))
		}
	} else if t.fieldKind == reflect.String {
		if objField.CanSet() {
			objField.Set(fieldValue)
		} else {
			objField = reflect.NewAt(objField.Type(), unsafe.Pointer(objField.UnsafeAddr())).Elem()
			objField.Set(fieldValue)
		}
	}
}

func (t *PrimaryKeyColumnMeta) DbColumnType() string {
	if t.fieldKind == reflect.Int32 {
		return "int"
	} else if t.fieldKind == reflect.Int64 {
		return "bigint"
	} else if t.fieldKind == reflect.String {
		return fmt.Sprintf("varchar(%d)", t.length)
	} else {
		panic(fmt.Sprintf("unsupport primary key type of %s %s", t.fieldName, t.fieldKind))
	}
}

func (t *PrimaryKeyColumnMeta) DefaultValue() string {
	return "NOT NULL"
}

func (t *PrimaryKeyColumnMeta) IsChange(dbColumnType string, dbColumnExtra string) bool {
	if t.strategy == metag.AUTO && dbColumnExtra != "auto_increment" {
		return true
	}
	if t.fieldKind == reflect.Int32 {
		return !strings.HasPrefix(dbColumnType, "int")
	} else if t.fieldKind == reflect.Int64 {
		return !strings.HasPrefix(dbColumnType, "bigint")
	} else {
		typeStr := fmt.Sprintf("varchar(%v)", t.length)
		return !strings.Contains(dbColumnType, typeStr)
	}
}

func (t *PrimaryKeyColumnMeta) Length() int32 {
	return t.length
}

func (t *PrimaryKeyColumnMeta) AutoBase() int64 {
	return t.autoBase
}

func NewPrimaryKeyField(structField reflect.StructField, columnMetag metag.ColumnMetag, primaryKeyMetag metag.PrimaryKeyMetag) *PrimaryKeyColumnMeta {
	var meta PrimaryKeyColumnMeta
	meta.fillBaseValue(structField, columnMetag)
	meta.strategy = primaryKeyMetag.KeyStrategy
	meta.length = columnMetag.Length
	if meta.length <= 0 {
		meta.length = 255
	}
	meta.autoBase = primaryKeyMetag.AutoBase
	return &meta
}
